﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace SharpMap.Presentation.Wpf
{
    public class DrawingLayer : FrameworkElement
    {
        static DrawingLayer()
        {
            DataContextProperty.OverrideMetadata(typeof(DrawingLayer), 
                new FrameworkPropertyMetadata(typeof(DrawingLayer)
                    ,FrameworkPropertyMetadataOptions.AffectsRender));
        }

        public static readonly DependencyProperty InversionScaleProperty =
            DependencyProperty.Register("InversionScale",typeof(double),typeof(DrawingLayer),
            new PropertyMetadata((double)1.0));


        public double InversionScale
        {
            get
            {
                return (double)GetValue(InversionScaleProperty);
            }
            set
            {
                SetValue(InversionScaleProperty,value);
            }
        }

        public static readonly DependencyProperty WpfViewTransformProperty
            = DependencyProperty.Register("WpfViewTransform",
                                        typeof(MatrixTransform),
                                        typeof(DrawingLayer),
                                        new FrameworkPropertyMetadata(
                                            MatrixTransform.Identity,
                                            FrameworkPropertyMetadataOptions.AffectsRender));

        public MatrixTransform WpfViewTransform
        {
            get
            {
              return   (MatrixTransform)GetValue(WpfViewTransformProperty);
            }
            set
            {
                SetValue(WpfViewTransformProperty, value);
            }
        }

        public ObservableCollection<Drawing> RenderedObjects
        {
            get { return (ObservableCollection<Drawing>)GetValue(RenderedObjectsProperty); }
            set { SetValue(RenderedObjectsProperty, value); }
        }

        public static readonly DependencyProperty RenderedObjectsProperty =
            DependencyProperty.Register("RenderedObjects", typeof(ObservableCollection<Drawing>), 
            typeof(DrawingLayer), 
            new FrameworkPropertyMetadata(
                default(ObservableCollection<Drawing>)
                ,FrameworkPropertyMetadataOptions.AffectsRender
                , RenderedObjectsPropertyChanged));

        static void RenderedObjectsPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            DrawingLayer This = target as DrawingLayer;
            INotifyCollectionChanged newDrawingSource = e.NewValue as
                INotifyCollectionChanged;

            INotifyCollectionChanged oldDrawingSource = e.NewValue as
                    INotifyCollectionChanged;
            if (oldDrawingSource != null)
            {
                oldDrawingSource.CollectionChanged -= This.drawingsCollectionChanged;
            }
            if (This != null && newDrawingSource != null)
            {
                newDrawingSource.CollectionChanged += 
                    This.drawingsCollectionChanged;

            }
        }


        //TODO: can be optimized
        void drawingsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            InvalidateVisual();
        }



        public string RenderedObjectsSourcePath
        {
            get { return (string)GetValue(RenderedObjectsSourcePathProperty); }
            set { SetValue(RenderedObjectsSourcePathProperty, value); }
        }
        public static readonly DependencyProperty RenderedObjectsSourcePathProperty =
            DependencyProperty.Register("RenderedObjectsSourcePath", typeof(string), typeof(DrawingLayer),
            new UIPropertyMetadata("RenderedObjects", RenderedObjectsSourcePathChanged));


        static void RenderedObjectsSourcePathChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            DrawingLayer This = target as DrawingLayer;
            if (This != null && e.NewValue != null)
            {
                BindingOperations.ClearBinding(This, DrawingLayer.RenderedObjectsProperty);
                Binding b = new Binding(This.RenderedObjectsSourcePath);
                b.Mode = BindingMode.OneWay;
                This.SetBinding(DrawingLayer.RenderedObjectsProperty, b);
            }
        }

        public DrawingLayer()
        {
            WireUpBindings();
            
        }

        protected override  void OnRender(DrawingContext drawingContext)
        {
            ObservableCollection<Drawing> renderedObject =
                DataContext as ObservableCollection<Drawing>;
            if (renderedObject == null)
            {
                return;
            }
            drawingContext.PushTransform(WpfViewTransform);
            foreach (Drawing drawing in renderedObject)
            {
                GeometryDrawing gd = drawing as GeometryDrawing;
                ImageDrawing imageDrawing = drawing as ImageDrawing;
                if (gd != null && gd.Pen != null)
                {
                    Pen scaledPen = new Pen
                                            {
                                                 Brush = gd.Pen.Brush
                                                ,Thickness = gd.Pen.Thickness * InversionScale
                                                ,StartLineCap = gd.Pen.StartLineCap
                                                ,EndLineCap = gd.Pen.EndLineCap
                                                ,DashCap = gd.Pen.DashCap
                                                ,LineJoin = gd.Pen.LineJoin
                                                ,MiterLimit = gd.Pen.MiterLimit
                                                ,DashStyle =gd.Pen.DashStyle
                                            };
                    drawingContext.DrawGeometry(gd.Brush, scaledPen, gd.Geometry);
                }
                if (imageDrawing != null)
                {
                    Size sz = new Size(imageDrawing.Rect.Width * InversionScale,
                                        imageDrawing.Rect.Height * InversionScale);
                     Rect rect = new Rect(imageDrawing.Rect.Location, sz);
                     drawingContext.DrawImage(imageDrawing.ImageSource, rect);
                }
            }
            drawingContext.Pop();
        }
        private void WireUpBindings()
        {
            Binding b = new Binding(".");
            b.Mode = BindingMode.OneWay;
            SetBinding(DrawingLayer.RenderedObjectsProperty, b);

            b = new Binding("ToViewTransform");
            b.Mode = BindingMode.OneWay;
            b.Converter = new Converters.MatrixToTransformConverter();
            SetBinding(WpfViewTransformProperty, b);

            b = new Binding("ToViewTransform");
            b.Mode = BindingMode.OneWay;
            b.Converter = new Converters.TransformToScaleConverter();
            SetBinding(InversionScaleProperty, b);
        }
    }
}
